#!/usr/bin/env python
# -*- coding: utf8 -*-
#
#    Copyright 2014,2018 Mario Gomez <mario.gomez@teubi.co>
#
#    This file is part of MFRC522-Python
#    MFRC522-Python is a simple Python implementation for
#    the MFRC522 NFC Card Reader for the Raspberry Pi.
#
#    MFRC522-Python is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    MFRC522-Python is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public License
#    along with MFRC522-Python.  If not, see <http://www.gnu.org/licenses/>.
#

import RPi.GPIO as GPIO
import spi
import signal
import time

class MFRC522:
	NRSTPD = 22

	MAX_LEN = 16

	PCD_IDLE       = 0x00
	PCD_AUTHENT    = 0x0E
	PCD_RECEIVE    = 0x08
	PCD_TRANSMIT   = 0x04
	PCD_TRANSCEIVE = 0x0C
	PCD_RESETPHASE = 0x0F
	PCD_CALCCRC    = 0x03

	PICC_REQIDL    = 0x26
	PICC_REQALL    = 0x52
	PICC_ANTICOLL  = 0x93
	PICC_SElECTTAG = 0x93
	PICC_AUTHENT1A = 0x60
	PICC_AUTHENT1B = 0x61
	PICC_READ      = 0x30
	PICC_WRITE     = 0xA0
	PICC_DECREMENT = 0xC0
	PICC_INCREMENT = 0xC1
	PICC_RESTORE   = 0xC2
	PICC_TRANSFER  = 0xB0
	PICC_HALT      = 0x50

	MI_OK       = 0
	MI_NOTAGERR = 1
	MI_ERR      = 2

	Reserved00     = 0x00
	CommandReg     = 0x01
	CommIEnReg     = 0x02
	DivlEnReg      = 0x03
	CommIrqReg     = 0x04
	DivIrqReg      = 0x05
	ErrorReg       = 0x06
	Status1Reg     = 0x07
	Status2Reg     = 0x08
	FIFODataReg    = 0x09
	FIFOLevelReg   = 0x0A
	WaterLevelReg  = 0x0B
	ControlReg     = 0x0C
	BitFramingReg  = 0x0D
	CollReg        = 0x0E
	Reserved01     = 0x0F

	Reserved10     = 0x10
	ModeReg        = 0x11
	TxModeReg      = 0x12
	RxModeReg      = 0x13
	TxControlReg   = 0x14
	TxAutoReg      = 0x15
	TxSelReg       = 0x16
	RxSelReg       = 0x17
	RxThresholdReg = 0x18
	DemodReg       = 0x19
	Reserved11     = 0x1A
	Reserved12     = 0x1B
	MifareReg      = 0x1C
	Reserved13     = 0x1D
	Reserved14     = 0x1E
	SerialSpeedReg = 0x1F

	Reserved20        = 0x20  
	CRCResultRegM     = 0x21
	CRCResultRegL     = 0x22
	Reserved21        = 0x23
	ModWidthReg       = 0x24
	Reserved22        = 0x25
	RFCfgReg          = 0x26
	GsNReg            = 0x27
	CWGsPReg          = 0x28
	ModGsPReg         = 0x29
	TModeReg          = 0x2A
	TPrescalerReg     = 0x2B
	TReloadRegH       = 0x2C
	TReloadRegL       = 0x2D
	TCounterValueRegH = 0x2E
	TCounterValueRegL = 0x2F

	Reserved30      = 0x30
	TestSel1Reg     = 0x31
	TestSel2Reg     = 0x32
	TestPinEnReg    = 0x33
	TestPinValueReg = 0x34
	TestBusReg      = 0x35
	AutoTestReg     = 0x36
	VersionReg      = 0x37
	AnalogTestReg   = 0x38
	TestDAC1Reg     = 0x39
	TestDAC2Reg     = 0x3A
	TestADCReg      = 0x3B
	Reserved31      = 0x3C
	Reserved32      = 0x3D
	Reserved33      = 0x3E
	Reserved34      = 0x3F

	serNum = []

	def __init__(self, dev='/dev/spidev0.0', spd=1000000):
		spi.openSPI(device=dev,speed=spd)
		GPIO.setmode(GPIO.BOARD)
		GPIO.setup(self.NRSTPD, GPIO.OUT)
		GPIO.output(self.NRSTPD, 1)
		self.MFRC522_Init()
	
	#MFRC522_Reset(self)
	def MFRC522_Reset(self):
		self.Write_MFRC522(self.CommandReg, self.PCD_RESETPHASE)
	
	#Write_MFRC522(self, addr, val)
	def Write_MFRC522(self, addr, val):
		spi.transfer(((addr<<1)&0x7E,val))
	
	#Read_MFRC522(self, addr)
	def Read_MFRC522(self, addr):
		val = spi.transfer((((addr<<1)&0x7E) | 0x80,0))
		return val[1]

	#SetBitMask(self, reg, mask)
	def SetBitMask(self, reg, mask):
		tmp = self.Read_MFRC522(reg)
		self.Write_MFRC522(reg, tmp | mask)

	#ClearBitMask(self, reg, mask)
	def ClearBitMask(self, reg, mask):
		tmp = self.Read_MFRC522(reg);
		self.Write_MFRC522(reg, tmp & (~mask))

	#AntennaOn(self)
	def AntennaOn(self):
		temp = self.Read_MFRC522(self.TxControlReg)
		if(~(temp & 0x03)):
			self.SetBitMask(self.TxControlReg, 0x03)

	#AntennaOff(self)
	def AntennaOff(self):
		self.ClearBitMask(self.TxControlReg, 0x03)

	#MFRC522_ToCard(self,command,sendData)
	def MFRC522_ToCard(self,command,sendData):
		backData = []
		backLen = 0
		status = self.MI_ERR
		irqEn = 0x00
		waitIRq = 0x00
		lastBits = None
		n = 0
		i = 0

		if command == self.PCD_AUTHENT:
			irqEn = 0x12
			waitIRq = 0x10
		if command == self.PCD_TRANSCEIVE:
			irqEn = 0x77
			waitIRq = 0x30

		self.Write_MFRC522(self.CommIEnReg, irqEn|0x80)
		self.ClearBitMask(self.CommIrqReg, 0x80)
		self.SetBitMask(self.FIFOLevelReg, 0x80)

		self.Write_MFRC522(self.CommandReg, self.PCD_IDLE);  

		while(i<len(sendData)):
			self.Write_MFRC522(self.FIFODataReg, sendData[i])
			i = i+1

		self.Write_MFRC522(self.CommandReg, command)

		if command == self.PCD_TRANSCEIVE:
			self.SetBitMask(self.BitFramingReg, 0x80)

		i = 2000
		while True:
			n = self.Read_MFRC522(self.CommIrqReg)
			i = i - 1
			if ~((i!=0) and ~(n&0x01) and ~(n&waitIRq)):
				break

		self.ClearBitMask(self.BitFramingReg, 0x80)

		if i != 0:
			if (self.Read_MFRC522(self.ErrorReg) & 0x1B)==0x00:
				status = self.MI_OK

				if n & irqEn & 0x01:
					status = self.MI_NOTAGERR

				if command == self.PCD_TRANSCEIVE:
					n = self.Read_MFRC522(self.FIFOLevelReg)
					lastBits = self.Read_MFRC522(self.ControlReg) & 0x07
					if lastBits != 0:
						backLen = (n-1)*8 + lastBits
					else:
						backLen = n*8

					if n == 0:
						n = 1
					if n > self.MAX_LEN:
						n = self.MAX_LEN

					i = 0
					while i<n:
						backData.append(self.Read_MFRC522(self.FIFODataReg))
						i = i + 1;
			else:
				status = self.MI_ERR

		return (status,backData,backLen)


	#MFRC522_Request(self, reqMode)
	def MFRC522_Request(self, reqMode):
		status = None
		backBits = None
		TagType = []

		self.Write_MFRC522(self.BitFramingReg, 0x07)

		TagType.append(reqMode);
		(status,backData,backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, TagType)

		if ((status != self.MI_OK) | (backBits != 0x10)):
			status = self.MI_ERR

		return (status,backBits)

	#MFRC522_Anticoll(self)
	def MFRC522_Anticoll(self):
		backData = []
		serNumCheck = 0

		serNum = []

		self.Write_MFRC522(self.BitFramingReg, 0x00)

		serNum.append(self.PICC_ANTICOLL)
		serNum.append(0x20)

		(status,backData,backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE,serNum)

		if(status == self.MI_OK):
			i = 0
			if len(backData)==5:
				while i<4:
					serNumCheck = serNumCheck ^ backData[i]
					i = i + 1
				if serNumCheck != backData[i]:
					status = self.MI_ERR
			else:
				status = self.MI_ERR

		return (status,backData)

	#CalulateCRC(self, pIndata)
	def CalulateCRC(self, pIndata):
		self.ClearBitMask(self.DivIrqReg, 0x04)
		self.SetBitMask(self.FIFOLevelReg, 0x80);
		i = 0
		while i<len(pIndata):
			self.Write_MFRC522(self.FIFODataReg, pIndata[i])
			i = i + 1
		self.Write_MFRC522(self.CommandReg, self.PCD_CALCCRC)
		i = 0xFF
		while True:
			n = self.Read_MFRC522(self.DivIrqReg)
			i = i - 1
			if not ((i != 0) and not (n&0x04)):
				break
		pOutData = []
		pOutData.append(self.Read_MFRC522(self.CRCResultRegL))
		pOutData.append(self.Read_MFRC522(self.CRCResultRegM))
		return pOutData

	#MFRC522_SelectTag(self, serNum)
	def MFRC522_SelectTag(self, serNum):
		backData = []
		buf = []
		buf.append(self.PICC_SElECTTAG)
		buf.append(0x70)
		i = 0
		while i<5:
			buf.append(serNum[i])
			i = i + 1
		pOut = self.CalulateCRC(buf)
		buf.append(pOut[0])
		buf.append(pOut[1])
		(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buf)

		if (status == self.MI_OK) and (backLen == 0x18):
			print("Size: " + str(backData[0]))
			return    backData[0]
		else:
			return 0
	
	#MFRC522_Auth(self, authMode, BlockAddr, Sectorkey, serNum)
	def MFRC522_Auth(self, authMode, BlockAddr, Sectorkey, serNum):
		buff = []

		# First byte should be the authMode (A or B)
		buff.append(authMode)

		# Second byte is the trailerBlock (usually 7)
		buff.append(BlockAddr)

		# Now we need to append the authKey which usually is 6 bytes of 0xFF
		i = 0
		while(i < len(Sectorkey)):
			buff.append(Sectorkey[i])
			i = i + 1
		i = 0

		# Next we append the first 4 bytes of the UID
		while(i < 4):
			buff.append(serNum[i])
			i = i +1

		# Now we start the authentication itself
		(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_AUTHENT,buff)

		# Check if an error occurred
		if not(status == self.MI_OK):
			print("AUTH ERROR!!")
		if not (self.Read_MFRC522(self.Status2Reg) & 0x08) != 0:
			print("AUTH ERROR(status2reg & 0x08) != 0")

		# Return the status
		return status

	#MFRC522_StopCrypto1(self)
	def MFRC522_StopCrypto1(self):
		self.ClearBitMask(self.Status2Reg, 0x08)

	#MFRC522_Read(self, blockAddr)
	def MFRC522_Read(self, blockAddr):
		recvData = []
		recvData.append(self.PICC_READ)
		recvData.append(blockAddr)
		pOut = self.CalulateCRC(recvData)
		recvData.append(pOut[0])
		recvData.append(pOut[1])
		(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, recvData)
		if not(status == self.MI_OK):
			print("Error while reading!")
		i = 0
		if len(backData) == 16:
			print("Sector "+str(blockAddr)+" "+str(backData))

	#MFRC522_Write(self, blockAddr, writeData)
	def MFRC522_Write(self, blockAddr, writeData):
		buff = []
		buff.append(self.PICC_WRITE)
		buff.append(blockAddr)
		crc = self.CalulateCRC(buff)
		buff.append(crc[0])
		buff.append(crc[1])
		(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buff)
		if not(status == self.MI_OK) or not(backLen == 4) or not((backData[0] & 0x0F) == 0x0A):
			status = self.MI_ERR

		print("%s backdata &0x0F == 0x0A %s" % (backLen, backData[0]&0x0F))
		if status == self.MI_OK:
			i = 0
			buf = []
			while i < 16:
				buf.append(writeData[i])
				i = i + 1
			crc = self.CalulateCRC(buf)
			buf.append(crc[0])
			buf.append(crc[1])
			(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE,buf)
			if not(status == self.MI_OK) or not(backLen == 4) or not((backData[0] & 0x0F) == 0x0A):
				print("Error while writing")
			if status == self.MI_OK:
				print("Data written")

	#MFRC522_DumpClassic1K(self, key, uid)
	def MFRC522_DumpClassic1K(self, key, uid):
		i = 0
		while i < 64:
			status = self.MFRC522_Auth(self.PICC_AUTHENT1A, i, key, uid)
			# Check if authenticated
			if status == self.MI_OK:
				self.MFRC522_Read(i)
			else:
				print("Authentication error")
			i = i+1

	#MFRC522_Init(self)
	def MFRC522_Init(self):
		GPIO.output(self.NRSTPD, 1)

		self.MFRC522_Reset();


		self.Write_MFRC522(self.TModeReg, 0x8D)
		self.Write_MFRC522(self.TPrescalerReg, 0x3E)
		self.Write_MFRC522(self.TReloadRegL, 30)
		self.Write_MFRC522(self.TReloadRegH, 0)

		self.Write_MFRC522(self.TxAutoReg, 0x40)
		self.Write_MFRC522(self.ModeReg, 0x3D)
		self.AntennaOn()
